//==================================================================
//  Copyright (C) 2006  Davide Pasca
//
//  This library is free software; you can redistribute it and/or
//  modify it under the terms of the GNU Lesser General Public
//  License as published by the Free Software Foundation; either
//  version 2.1 of the License, or (at your option) any later version.
//
//  This library is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
//  Lesser General Public License for more details.
//
//  You should have received a copy of the GNU Lesser General Public
//  License along with this library; if not, write to the Free Software
//  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
//==================================================================
///
///
///
///
///
//==================================================================

#include "stdafx.h"

#include <math.h>
#include <float.h>
#include "rendm5d_scene.h"
#include "rendm5d_texture.h"
#include "rendm5d_mate.h"
#include "rendm5d_mesh.h"
#include "rendm5d_pntsprt.h"
#include "rendm5d_displist.h"

//==================================================================
using namespace PMath;

//==================================================================
RendM5D::Scene::Scene( RendDevice *rdevicep, const char *namep, const TCHAR *dirpathp ) :
	_RDevicep(rdevicep),
	M5D::Scene( namep, dirpathp ),
	_cur_mcamerap(NULL),
	_cur_secondary_camp(NULL),
	//_cur_main_lightp(NULL),
	_rend_off_x(0),
	_rend_off_y(0)
{
}

//==================================================================
RendM5D::Scene::~Scene()
{
}

//==================================================================
void RendM5D::Scene::OnTextureCreate( M5D::Texture *texp )
{
	RendM5D::Texture *rtexp = new RendM5D::Texture( texp, _RDevicep );
}
//==================================================================
void RendM5D::Scene::OnTextureDelete( M5D::Texture *mobjp )
{
	RendM5D::Texture *robjp = (RendM5D::Texture *)mobjp->_userdatap;
	SAFE_DELETE( robjp );
}

//==================================================================
void RendM5D::Scene::OnMaterialCreate( M5D::Material *matep )
{
	new RendM5D::Material( matep, _RDevicep );
}
//==================================================================
void RendM5D::Scene::OnMaterialDelete( M5D::Material *mobjp )
{
	RendM5D::Material *robjp = (RendM5D::Material *)mobjp->_userdatap;
	SAFE_DELETE( robjp );
}

//==================================================================
void RendM5D::Scene::OnModelCreate( M5D::Model *modelp )
{
	switch ( modelp->GetType() )
	{
	case M5D::Model::TYPE_MESH:
		new RendM5D::Mesh( (M5D::Mesh *)modelp, _RDevicep );
		break;
	case M5D::Model::TYPE_PNTSPRT:
		new RendM5D::PntSprt( (M5D::PntSprt *)modelp, _RDevicep );
		break;
	}
}
//==================================================================
void RendM5D::Scene::OnModelDelete( M5D::Model *modelp )
{
	switch ( modelp->GetType() )
	{
	case M5D::Model::TYPE_MESH:
		{
			RendM5D::Mesh *robjp = (RendM5D::Mesh *)modelp->_userdatap;
			PASSERT( robjp != NULL );
			SAFE_DELETE( robjp );
		}
		break;

	case M5D::Model::TYPE_PNTSPRT:
		{
			RendM5D::PntSprt *robjp = (RendM5D::PntSprt *)modelp->_userdatap;
			PASSERT( robjp != NULL );
			SAFE_DELETE( robjp );
		}
		break;

	case M5D::Model::TYPE_CAMERA:
		if ( modelp == _cur_mcamerap )
			_cur_mcamerap = NULL;
		break;

	case M5D::Model::TYPE_LIGHT:
		//if ( modelp == _cur_main_lightp )
		//	_cur_main_lightp = NULL;
		break;
	}
}

//==================================================================
void RendM5D::Scene::OnDispListCreate( M5D::DispList *mobjp )
{
	new RendM5D::DispList( mobjp, _RDevicep );
}
//==================================================================
void RendM5D::Scene::OnDispListDelete( M5D::DispList *mobjp )
{
	RendM5D::DispList *robjp = (RendM5D::DispList *)mobjp->_userdatap;
	SAFE_DELETE( robjp );
}

/*
//==================================================================
void RendM5D::Mesh::SuperDeleteCB_s( M5D::Scene *mscenep, void *objp )
{
	PASSERT( (M5D::Scene *)mmodelp == (RendM5D::Scene *)objp );	// paranoia
	delete (RendM5D::Scene *)objp;
}
*/

//==================================================================
void RendM5D::Scene::prepareLights()
{
	LightMap	&lightmap = _lightmap;

	lightmap.ResetLights();

	//_cur_main_lightp = NULL;
	bool	main_light_done = false;

	for (int i=0; i < GetModelsN(); ++i)
	{
		M5D::Model	*mmodelp = _models[i];

		switch ( mmodelp->GetType() )
		{
		case M5D::Model::TYPE_LIGHT:
			{
				M5D::Light		*mlightp = (M5D::Light *)mmodelp;

				if NOT( main_light_done )
				{
					main_light_done = true;
					_cur_main_light_ws_pos = mlightp->CalcGlobalPos();
				}

				lightmap.DrawLight( mlightp->GetColor(), mlightp->CalcGlobalPos() );
			}
			break;
		}
	}

	lightmap.Update( &_cur_mcamerap->_cur_cam_world_33_m );
	_cur_main_light_view_pos = _cur_mcamerap->_cur_cam_world_33_m * _cur_main_light_ws_pos;
}

//==================================================================
void RendM5D::Scene::prepareCamera()
{
//	_cur_cam_invmat = _cur_mcamerap->GetMatrix().GetBackTransformed();
	_RDevicep->_ShaderMng.SetProjMatrix( _cur_mcamerap->GetProjMatrix() );

//	_cur_proj_world_m = _cur_mcamerap->GetProjMatrix() * _cur_cam_invmat;

	int		viewport[4];
	float	minmax_z[2];
	_cur_mcamerap->GetViewport( viewport, minmax_z );

	_RDevicep->_DrawState.SetViewport(	_rend_off_x + viewport[0],
										_rend_off_y + viewport[1],
										viewport[2],
										viewport[3],
										minmax_z[0],
										minmax_z[1] );
}

//==================================================================
inline void RendM5D::Scene::selectRendMesh( RendM5D::Mesh *rmeshp, const RendM5D::Params &rparams )
{
	_RDevicep->_ShaderMng.SetModelviewMatrix0( rmeshp->_view_obj_m );
	_RDevicep->_ShaderMng.LoadMainMatrices( rparams._proj_obj_m_shparam,
											rparams._view_obj_m_shparam );

	if ( _cur_secondary_camp )
		_RDevicep->_ShaderMng.SetMatrix44(
								_cur_secondary_cam_lproj_obj_m_shparam,
								rmeshp->_secondary_proj_obj_m );
}

//==================================================================
inline void RendM5D::Scene::selectRendPntSprt( RendM5D::PntSprt *robjp, const RendM5D::Params &rparams )
{
	_RDevicep->_ShaderMng.SetModelviewMatrix0( robjp->_view_obj_m );
	_RDevicep->_ShaderMng.LoadMainMatrices( rparams._proj_obj_m_shparam,
											rparams._view_obj_m_shparam );
}

//==================================================================
inline void RendM5D::Scene::prepareMeshTransforms(	RendM5D::Mesh *rmeshp,
												const Matrix44 &world_obj_m,
												const Matrix44 lightmap_rgb_mats[3] )
{
	rmeshp->_view_obj_m = _cur_mcamerap->_cur_cam_world_m * rmeshp->_mmeshp->_mod_global_m;
	
/* ---- local space lighting
	Matrix44	world_obj_m33 = world_obj_m;
	world_obj_m33._m14 = 0;
	world_obj_m33._m24 = 0;
	world_obj_m33._m34 = 0;
	world_obj_m33.GetRow3(0)->Normalize();
	world_obj_m33.GetRow3(1)->Normalize();
	world_obj_m33.GetRow3(2)->Normalize();
	world_obj_m33.GetRow4(3)->Set( 0, 0, 0, 1 );

	Matrix44	obj_world_m33 = world_obj_m33.GetTranspose33();
	rmeshp->_light_rgb_mats[0] = obj_world_m33 * lightmap_rgb_mats[0] * world_obj_m33;
	rmeshp->_light_rgb_mats[1] = obj_world_m33 * lightmap_rgb_mats[1] * world_obj_m33;
	rmeshp->_light_rgb_mats[2] = obj_world_m33 * lightmap_rgb_mats[2] * world_obj_m33;

	Matrix44	obj_world_m = world_obj_m.GetBackTransformed();

	rmeshp->_os_cam_pos = obj_world_m * *_cur_mcamerap->GetMatrix().GetRow4(3);
	rmeshp->_os_light_dir = obj_world_m * _cur_main_light_ws_pos;//obj_world_m * _cur_main_lightp->GetLocalPos();
*/

	// ---- world space lighting
	rmeshp->_light_rgb_mats[0] = lightmap_rgb_mats[0];
	rmeshp->_light_rgb_mats[1] = lightmap_rgb_mats[1];
	rmeshp->_light_rgb_mats[2] = lightmap_rgb_mats[2];

	if ( _cur_secondary_camp )
	{
		rmeshp->_secondary_proj_obj_m = _cur_secondary_camp->_cur_proj_world_m * rmeshp->_mmeshp->_mod_global_m;
	}
}

//==================================================================
void RendM5D::Scene::preparePntSprtTransforms( RendM5D::PntSprt *rpntsprtp, const Matrix44 &world_m )
{
	rpntsprtp->_view_obj_m = _cur_mcamerap->_cur_cam_world_m * rpntsprtp->_mpntsprtp->_mod_global_m;
/*
	Matrix44	world_obj_m33 = world_m;
	world_obj_m33.GetRow3(0)->Normalize();
	world_obj_m33.GetRow3(1)->Normalize();
	world_obj_m33.GetRow3(2)->Normalize();
	world_obj_m33.GetRow4(3)->Set( 0, 0, 0, 1 );

	Matrix44	obj_world_m33 = world_obj_m33.GetTranspose33();
	rpntsprtp->_light_rgb_mats[0] = obj_world_m33 * lightmap_rgb_mats[0] * world_obj_m33;
	rpntsprtp->_light_rgb_mats[1] = obj_world_m33 * lightmap_rgb_mats[1] * world_obj_m33;
	rpntsprtp->_light_rgb_mats[2] = obj_world_m33 * lightmap_rgb_mats[2] * world_obj_m33;
*/
}

//==================================================================
void RendM5D::Scene::prepareRenderables( RendM5D::Params &rparams )
{
	const Matrix44	*lmap_rgb_matsp = _lightmap.GetRGBLightMatrices();

	// need to preserve this !
	u_int		old_cur_rend_id = rparams.cur_rend_id;
	
	for (int i=0; i < GetModelsN(); ++i)
	{
		M5D::Mesh		*mmeshp = NULL;

		if ( rparams._tmp_vismap.Check( i ) )
		{
			M5D::Model		*mmodelp = _models[i];

			if NOT( mmodelp->IsVisible() )
				rparams._tmp_vismap.SetOff( i );
			else
			{
				switch ( mmodelp->GetType() )
				{
				case M5D::Model::TYPE_MESH:
					{
						mmeshp = (M5D::Mesh *)mmodelp;
						RendM5D::Mesh	*rmeshp = (RendM5D::Mesh *)mmeshp->_userdatap;
						prepareMeshTransforms( rmeshp, mmodelp->_mod_global_m, lmap_rgb_matsp );
						
						if ( rparams.render_type == RENDTYPE_STANDARD )
							rmeshp->PrepareStandard( rparams );
						else
							rmeshp->PrepareSpecial( rparams );
					}
					break;

				case M5D::Model::TYPE_PNTSPRT:
					{
						M5D::PntSprt		*mobjp = (M5D::PntSprt *)mmodelp;
						RendM5D::PntSprt	*robjp = (RendM5D::PntSprt *)mobjp->_userdatap;
						preparePntSprtTransforms( robjp, mmodelp->_mod_global_m );
						robjp->PreRender();
					}
					break;

				default:	// only mesh and point sprites are visible
					rparams._tmp_vismap.SetOff( i );
					break;
				}
			}
		}

		if ( rparams.render_type != RENDTYPE_STANDARD )
		{
			if ( rparams.render_type == RENDTYPE_TRI_ID )
				rparams.cur_rend_id += (mmeshp ? mmeshp->GetTrigs()._list.size() : 0);
			else
				if ( rparams.render_type == RENDTYPE_TRI_ONOFFCOL )
					rparams.tri_onoff_idxbase += (mmeshp ? mmeshp->GetTrigs()._list.size() : 0);
		}
	}

	rparams.cur_rend_id = old_cur_rend_id;
}

//==================================================================
void RendM5D::Scene::renderOpaqueCustomMesh( RendM5D::Params &rparams )
{
	RendM5D::Mesh::BeginRenderState_s( _RDevicep, rparams, true );
	for (int i=0; i < GetModelsN(); ++i)
	{
		M5D::Model		*mmodelp = _models[i];
		M5D::Mesh		*mmeshp = NULL;
		RendM5D::Mesh	*rmeshp = NULL;

		if ( rparams._tmp_vismap.Check( i ) )
		{
			switch ( mmodelp->GetType() )
			{
			case M5D::Model::TYPE_MESH:
				{
					mmeshp = (M5D::Mesh *)mmodelp;
					rmeshp = (RendM5D::Mesh *)mmeshp->_userdatap;
					selectRendMesh( rmeshp, rparams );

					if ( rparams.render_type == RENDTYPE_MODEL_ID )
						rparams.cur_rend_id = i+1;	// model ID (model idx + 1, 0 == empty)

					rmeshp->RenderSpecial( rparams );
				}
				break;
			}
		}

		if ( rparams.render_type == RENDTYPE_TRI_ID )
			rparams.cur_rend_id += (mmeshp ? mmeshp->GetTrigs()._list.size() : 0);
		else
			if ( rparams.render_type == RENDTYPE_TRI_ONOFFCOL )
				rparams.tri_onoff_idxbase += (mmeshp ? mmeshp->GetTrigs()._list.size() : 0);
	}
	RendM5D::Mesh::EndRenderState_s( _RDevicep );
}

//==================================================================
void RendM5D::Scene::renderStandardMeshOpaquePass( RendM5D::Params &rparams )
{
	// render meshes
	RendM5D::Mesh::BeginRenderState_s( _RDevicep, rparams, true );
	for (int i=0; i < GetModelsN(); ++i)
	{
		M5D::Model		*mmodelp = _models[i];
		M5D::Mesh		*mmeshp = NULL;
		RendM5D::Mesh	*rmeshp = NULL;

		if ( rparams._tmp_vismap.Check( i ) )
		{
			switch ( mmodelp->GetType() )
			{
			case M5D::Model::TYPE_MESH:
				{
					mmeshp = (M5D::Mesh *)mmodelp;
					rmeshp = (RendM5D::Mesh *)mmeshp->_userdatap;
					selectRendMesh( rmeshp, rparams );

					bool skipped_anything = rmeshp->RenderStandard( rparams, true );
					// see if we had to skip rendering because of translucent material
					if NOT( skipped_anything )
						rparams._tmp_vismap.SetOff( i );
				}
				break;
			}
		}
	}
	RendM5D::Mesh::EndRenderState_s( _RDevicep );
}

//==================================================================
void RendM5D::Scene::renderStandardMeshTranslucentPass( RendM5D::Params &rparams )
{
	// render meshes
	RendM5D::Mesh::BeginRenderState_s( _RDevicep, rparams, false );
	for (int i=0; i < GetModelsN(); ++i)
	{
		M5D::Model		*mmodelp = _models[i];
		M5D::Mesh		*mmeshp = NULL;
		RendM5D::Mesh	*rmeshp = NULL;

		if ( rparams._tmp_vismap.Check( i ) )
		{
			if ( mmodelp->GetType() == M5D::Model::TYPE_MESH )
			{
				mmeshp = (M5D::Mesh *)mmodelp;
				rmeshp = (RendM5D::Mesh *)mmeshp->_userdatap;
				selectRendMesh( rmeshp, rparams );
				rmeshp->RenderStandard( rparams, false );
			}
		}
	}
	RendM5D::Mesh::EndRenderState_s( _RDevicep );
}

//==================================================================
void RendM5D::Scene::PreRender( RendM5D::Params &rparams )
{
	if NOT( _cur_mcamerap )
	{	// need a camera to render anything !
		//PASSERT( 0 );
		PDEBUG_PRINTF( _T("Camera is not set !!!\n") );
		return;
	}

	// set the standard matrix params in case custom ones are being provided
	if ( rparams._proj_obj_m_shparam == 0 )
		rparams._proj_obj_m_shparam = _RDevicep->_ShaderMng._Std._VSP_U_proj_obj_m;

	if ( rparams._view_obj_m_shparam == 0 )
		rparams._view_obj_m_shparam = _RDevicep->_ShaderMng._Std._VSP_U_view_obj_m;

	_cur_secondary_camp = rparams._secondary_camp;
	_cur_secondary_cam_lproj_obj_m_shparam = rparams._secondary_cam_lproj_obj_m_shparam;

	prepareCamera();

	_RDevicep->_DrawState.Reset();

	if ( rparams.use_lights )
	{
		prepareLights();
		_RDevicep->_ShaderMng._Std.SetSHLightMatrices(	_lightmap.GetRGBLightMatrices(), _cur_main_light_view_pos );
	}

	if ( IsFogEnabled() )
	{
		_RDevicep->_ShaderMng._Std.SetFogColor( GetFogColor() );
		_RDevicep->_ShaderMng._Std.SetFogRange( GetFogStartDepth(),
												 GetFogEndDepth() );
	}
	else
	{
		_RDevicep->_ShaderMng._Std.SetFogColor( Vector3(1,0,1) );
		_RDevicep->_ShaderMng._Std.SetFogRange( 10e+20, 10e+20 );
	}

	int		n_models = GetModelsN();
	PASSERT( n_models <= sizeof(rparams._tmp_vismap_buffer)*8 );
	rparams._tmp_vismap.Setup( rparams._tmp_vismap_buffer, n_models );

	if ( rparams.model_vismap.GetMapPtr() )
	{
		PASSERT( rparams.model_vismap.GetN() >= n_models );
		memcpy( rparams._tmp_vismap.GetMapPtr(), rparams.model_vismap.GetMapPtr(), n_models );
	}
	else
		rparams._tmp_vismap.Fill();	// set all visible

	prepareRenderables( rparams );

	rparams._is_prepared = true;
}

//==================================================================
void RendM5D::Scene::renderParticles( RendM5D::Params &rparams )
{
	// render point sprites
	RendM5D::PntSprt::BeginRenderState_s( _RDevicep );
		for (int i=0; i < GetModelsN(); ++i)
		{
			M5D::Model	*mmodelp = _models[i];

			if ( rparams._tmp_vismap.Check( i ) )
			{
				switch ( mmodelp->GetType() )
				{
				case M5D::Model::TYPE_PNTSPRT:
					{
						M5D::PntSprt		*mobjp = (M5D::PntSprt *)mmodelp;
						RendM5D::PntSprt	*robjp = (RendM5D::PntSprt *)mobjp->_userdatap;
						selectRendPntSprt( robjp, rparams );
						robjp->Render();
					}
					break;
				}
			}
		}
	RendM5D::PntSprt::EndRenderState_s( _RDevicep );
}

//==================================================================
void RendM5D::Scene::Render( RendM5D::Params &rparams )
{
	if NOT( _cur_mcamerap )
	{	// need a camera to render anything !
		//PASSERT( 0 );
		return;
	}

	PASSERT( rparams._is_prepared == true );
	if NOT( rparams._is_prepared )
		return;

	if ( rparams.render_type == RENDTYPE_STANDARD )
	{
		renderStandardMeshOpaquePass( rparams );
		renderParticles( rparams );
		renderStandardMeshTranslucentPass( rparams );
	}
	else
	{
		renderOpaqueCustomMesh( rparams );
		renderParticles( rparams );
	}

	if ( rparams.onface_meshp && rparams.oneface_tri_idx >= 0 )
	{
		M5D::Mesh		*mmeshp = (M5D::Mesh *)rparams.onface_meshp;

		// render meshes
		RendM5D::Mesh::BeginRenderState_s( _RDevicep, rparams, false );

			RendM5D::Mesh	*rmeshp = (RendM5D::Mesh *)mmeshp->_userdatap;
			selectRendMesh( rmeshp, rparams );

			_RDevicep->_DrawState.SetBlending(	true,
											PSYSGFX::BLENDFCT_SRC_ALPHA,
											PSYSGFX::BLENDFCT_ONE_MINUS_SRC_ALPHA );

			_RDevicep->_ShaderMng._Std.SelectShader( false, false, false );
			_RDevicep->_ShaderMng._Std.SetBaseColor( Vector4(1,0,0,0.4f),
													  Vector3(0,0,0), 0,
													  Vector4(0,0,0,0) );

			_RDevicep->_ShaderMng.SelectVertexStream(	mmeshp->GetVerts().GetPosR(),
														NULL,
														NULL,
														NULL,
														sizeof(*mmeshp->GetVerts().GetPosR()) );

			const M5D::Triangle	&tri = mmeshp->GetTrigs()._list[ rparams.oneface_tri_idx ];
			u_short	indices[3] = { tri.pos[0], tri.pos[1], tri.pos[2] };

#if defined(TARGET_D3D)
			D3DDevice	*d3ddevp = _RDevicep->GetD3DDevice();

			d3ddevp->DrawIndexedPrimitiveUP(	D3DPT_TRIANGLELIST,
												0,
												mmeshp->GetVerts().GetPosN(),
												1,
												indices,
												D3DFMT_INDEX16,
												(u_char *)mmeshp->GetVerts().GetPos(),
												sizeof(*mmeshp->GetVerts().GetPos()) );
#elif defined(TARGET_OPENGL)
			glDrawElements( GL_TRIANGLES, 3, GL_UNSIGNED_SHORT, indices );
#endif
//			rmeshp->Render( rparams );

		RendM5D::Mesh::EndRenderState_s( _RDevicep );
	}
}

//==================================================================
static void vertsFromBBox( Vector4 verts[8], const Vector3 &bbmin, const Vector3 &bbmax )
{
	verts[0].Set( bbmin._x, bbmin._y, bbmin._z );
	verts[1].Set( bbmax._x, bbmin._y, bbmin._z );
	verts[2].Set( bbmax._x, bbmax._y, bbmin._z );
	verts[3].Set( bbmin._x, bbmax._y, bbmin._z );

	verts[4].Set( bbmin._x, bbmin._y, bbmax._z );
	verts[5].Set( bbmax._x, bbmin._y, bbmax._z );
	verts[6].Set( bbmax._x, bbmax._y, bbmax._z );
	verts[7].Set( bbmin._x, bbmax._y, bbmax._z );
}

//==================================================================
void RendM5D::Scene::CalcRendBox( float bbmin[2], float bbmax[2] )
{
	Vector4		verts[8];
	//Matrix44	proj_world_m;

	prepareCamera();
/*
	double	projection[16];
	glGetDoublev( GL_PROJECTION_MATRIX, projection );

	proj_world_m.CopyFromOGL( projection );
*/
	bbmin[0] =  FLT_MAX;
	bbmin[1] =  FLT_MAX;
	bbmax[0] = -FLT_MAX;
	bbmax[1] = -FLT_MAX;

	//proj_world_m = _cur_mcamerap->GetProjMatrix() * _cur_cam_invmat;

	const Matrix44	&proj_world_m = _cur_mcamerap->_cur_proj_world_m;

	for (int i=0; i < GetModelsN(); ++i)
	{
		M5D::Model	*mmodelp = _models[i];

		if ( mmodelp->GetType() == M5D::Model::TYPE_MESH )
		{
			M5D::Mesh		*mmeshp = (M5D::Mesh *)mmodelp;
//			RendM5D::Mesh	*rmeshp = (RendM5D::Mesh *)mmodelp->GetUserData();

			if ( mmeshp->HasGeometry() && mmeshp->IsVisible() )
			{
				vertsFromBBox( verts, mmeshp->GetBBoxMin(), mmeshp->GetBBoxMax() );

				Matrix44	tmat = proj_world_m * mmodelp->_mod_global_m;
				for (int j=0; j < 8; ++j)
				{
					Vector4	tvert;
					tvert = tmat * verts[j];

					float	oow = 1.0f / tvert._w;
					tvert._x *= oow;
					tvert._y *= oow;
					
					if ( tvert._x  < bbmin[0] )	bbmin[0] = tvert._x;
					if ( tvert._x  > bbmax[0] )	bbmax[0] = tvert._x;
					
					if ( tvert._y  < bbmin[1] )	bbmin[1] = tvert._y;
					if ( tvert._y  > bbmax[1] )	bbmax[1] = tvert._y;
				}
			}
		}
	}
}
